using System;
using System.Linq;
using System.Linq.Expressions;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Linq;
using Rob.SampleCode.NHibernate.Core;

namespace Rob.SampleCode.NHibernate.Infrastructure
{
    public class NHibernateRepository : IRepository
    {
        public NHibernateRepository(NHibernateUnitOfWork uow)
        {
            _uow = uow;
        }

        private readonly NHibernateUnitOfWork _uow;

        public T Get<T>(int id)
        {
            return _uow.Session.Get<T>(id);
        }

        public T Get<T, K>(K id) where T : IKeyed<K>
        {
            return _uow.Session.Get<T>(id);
        }

        public T Get<T>(Expression<Func<T, bool>> predicate)
        {
            return Find<T>().SingleOrDefault(predicate);
        }

        public IQueryable<T> Find<T>()
        {
            return _uow.Session.Query<T>();
        }

        public IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate)
        {
            return Find<T>().Where(predicate);
        }

        public T Add<T>(T entity)
        {
            _uow.Session.Save(entity);
            return entity;
        }

        public T Remove<T>(T entity)
        {
            _uow.Session.Delete(entity);
            return entity;
        }
    }

    public class NHibernateUnitOfWork
    {
        private static readonly ISessionFactory _sessionFactory;

        static NHibernateUnitOfWork()
        {
            _sessionFactory = Fluently
                .Configure()
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(cfg => cfg.FromConnectionStringWithKey("db")).ShowSql())
                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CategoryMap>())
                .BuildSessionFactory();
        }

        /****************************************************************/
        /****************************************************************/

        private ISession _session;

        public ISession Session
        {
            get
            {
                if (_session == null)
                {
                    _session = _sessionFactory.OpenSession();
                    _session.FlushMode = FlushMode.Commit;
                    _session.BeginTransaction();
                }

                return _session;
            }
        }

        public void End()
        {
            if (_session == null)
                return;

            using (_session)
                _session.Transaction.Commit();

            _session = null;
        }
    }
}